#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  README patchlevel.h Makefile uncgi.c
# Wrapped by koreth@gerbilmeister on Wed Oct 19 00:50:17 1994
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(7639 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X
X                             UN-CGI VERSION 1.2
X                                       
XContents
X
X     * Introduction
X     * Installation
X     * Usage
X     * Other Features
X     * Bugs
X     * Frequently Asked Questions
X       
XIntroduction
X
X   
X   
X   This is uncgi 1.1, a frontend for processing queries and forms from
X   the Web on UNIX systems. You can get it via anonymous ftp from
X   ftp.hyperion.com or, depending on your browser, by following this
X   link.
X   
X   Without this program, if you wanted to process a form, you'd have to
X   either write or dig up routines to translate the values of the form's
X   fields from "URL encoding" to whatever your program required. This was
X   a hassle in C, and a real pain in the shell, and you had do things
X   differently for GET and POST queries.
X   
X   Which is where uncgi comes in. It decodes all the form fields and
X   sticks them into environment variables for easy perusal by a shell
X   script, a C program, a Perl script, or whatever you like, then
X   executes whatever other program you specify.
X   
X   (Actually, "uncgi" is something of a misnomer, as the weird URL syntax
X   is from the HTML forms specification, not from CGI itself.)
X   
XInstallation
X
X   
X   
X   To install, edit the Makefile, setting "CGI_BIN" to point to your HTTP
X   server's CGI directory. Under NCSA httpd, this is usually a directory
X   called "cgi-bin" in the server root directory. Then run "make
X   install".
X   
X   Note that the variable serves two purposes as supplied: it tells the
X   Makefile where to install uncgi, and it also tells uncgi where to look
X   for backend scripts. If you would like uncgi to look in a directory
X   other than the one it's installed in, set DESTDIR to the install
X   directory and CGI_BIN to the directory containing the scripts and
X   programs that uncgi will be executing.
X   
XUsage
X
X   
X   
X   An example is the easiest way to demonstrate uncgi's use. Suppose you
X   have the following in an HTML file:
X
X<form method=POST action="/cgi-bin/uncgi/myscript/thanks.html">
XWhat's your name?
X<input type=text size=30 name=name>
X<p>
XType some comments.
X<br>
X<textarea name=comments rows=10 cols=60></textarea>
XWhat problem are you having? <select name=problem multiple>
X<option> Can't sleep
X<option> Unruly goat
X<option> Limousine overcrowding
X</select>
X<p>
X<input type=submit value="  Send 'em in!  ">
X</form>
X
X   When the user selects the "Send 'em in!" button, the HTTP server will
X   run uncgi. Uncgi will set three environment variables, WWW_name,
X   WWW_comments and WWW_problem, to the values of the "name", "comments",
X   and "problem" fields in the form, respectively. Then it will execute
X   myscript in the CGI_BIN directory. If more than one "problem" is
X   selected, the values will all be placed in WWW_comments, separated by
X   hash marks ('#').
X   
X   All the usual CGI environment variables (PATH_INFO, QUERY_STRING,
X   etc.) are available to the script or program you tell uncgi to run. A
X   couple of them (PATH_INFO and PATH_TRANSLATED) are tweaked by uncgi to
X   the values they'd have if your program were being executed directly by
X   the server. In the above example, when uncgi ran myscript, PATH_INFO
X   would be set to "/thanks.html". This is an easy way to specify
X   additional parameters to your script without resorting to hidden
X   fields.
X   
X   Myscript might be as simple as this:
X
X#!/bin/sh
Xecho 'Content-type: text/html'
Xecho ''
Xmail webmaster << __EOF__
X$WWW_name said:
X$WWW_comments
X__EOF__
Xcat $PATH_TRANSLATED
X
X   With uncgi, that's all you need to do to write a script to send you
X   mail from a form and print a prewritten file as a response. And it's
X   the same whether you want to use GET or POST queries.
X   
XOther Features
X
X   
X   
X   Extra feature: If you compile with -DNO_MAIN, you can use uncgi as a
X   library function in a C program of your own. Just call uncgi() at the
X   start of your program.
X   
X   Uncgi will handle hybrid GET/POST requests. Specify a method of POST
X   in the form, and add a GET-style query string to the action, for
X   example <form method=POST
X   action="/cgi-bin/uncgi/myscript?form_id=feedback">. When your script
X   is run, WWW_form_id will be set to "feedback". This will only work if
X   your HTTP server supports it (NCSA's does, for now anyway.)
X   
XBugs
X
X   
X   
X   There should be a way to specify a list of directories that uncgi will
X   search for backend scripts.
X   
XFrequently Asked Questions
X
X  WHERE DO I PUT EVERYTHING?
X  
X   
X   
X   Short answer: Your server's cgi-bin directory.
X   
X   Long answer: When you edit uncgi's Makefile, you'll see two macro
X   definitions. The first, CGI_BIN, tells uncgi where to look for your
X   scripts or programs. It must be set to the name of an existing
X   directory; you can set it to any directory you like. Usually it's set
X   to the location of your server's cgi-bin directory.
X   
X   The second, DESTDIR, tells the Makefile where to install uncgi. By
X   default, it's set to the same thing CGI_BIN is. It must point to your
X   server's cgi-bin directory, so you'll need to change it if you set
X   CGI_BIN to something else.
X   
X  WHY DOES UNCGI TELL ME IT CAN'T RUN MY SCRIPT?
X  
X   
X   
X   First, make sure your script is in the right place; see the preceding
X   section.
X   
X   Second, make sure your script can be executed by the server. Remember,
X   the server probably isn't running from your account, so you need to
X   set the permissions on your script such that it can be run by any
X   user. Usually, you can say "chmod 755 scriptname" (replacing
X   scriptname with the name of your script) to set the permissions
X   properly.
X   
X  WHAT DO I DO IN MY FORMS?
X  
X   
X   
X   The <form> tag has two attributes, METHOD and ACTION. METHOD must be
X   set to either GET or POST; uncgi will handle either one, but POST is
X   preferred if you have textarea fields or are expecting a lot of
X   information from the client. Note that the attribute name (METHOD) can
X   be upper or lower case, but the value must be all caps.
X   
X   ACTION should have the following components:
X     * The alias for your server's cgi-bin directory (usually just
X       /cgi-bin/)
X     * uncgi/ to tell the server which program to execute.
X     * The name of your script as it appears in the directory where you
X       told uncgi to find your scripts and programs (the CGI_BIN macro in
X       the Makefile.) Example: myscript
X     * Optionally, a forward slash followed by additional parameters,
X       often the path to another file you want your script to print. This
X       path information will be available to your script in the PATH_INFO
X       and PATH_TRANSLATED environment variables (the latter contains the
X       full path to the document, including the path to the server root
X       directory.) Note that you can't use tilde notation. Example:
X       /form_output/acknowledge.html
X       
X   
X   
X   So, if you wanted to tell uncgi to run sendmemail with no additional
X   parameters, you'd put sendmemail in the directory you specified as the
X   value of CGI_BIN in uncgi's makefile, and use the following tag to
X   begin your form:
X   
X     <form method=POST action="/cgi-bin/uncgi/sendmemail">
X     
X  I GET AN ERROR CODE 500 FROM THE SERVER.
X  
X   
X   
X   This usually means your script isn't specifying a content type to the
X   server. The first thing your script needs to output is:
X   
X     Content-type: text/html
X     
X   followed by a blank line.
X     _________________________________________________________________
X   
X      Maintained by Steven Grimm <koreth@hyperion.com>.
X      Send mail if you have comments or suggestions.
END_OF_FILE
if test 7639 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(22 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X#define VERSION "1.2"
END_OF_FILE
if test 22 -ne `wc -c <'patchlevel.h'`; then
    echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(521 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# @(#)Makefile	1.2 8/21/94
X#
X# Makefile for "uncgi"
X#
X
X#
X# Set this to the directory where your CGI scripts live.  Uncgi will look
X# there for backend scripts.
X#
XCGI_BIN = /home/gerbilmeister/www/cgi-bin
X
X#
X# Set this to the directory you'd like to install uncgi in, if different
X# from CGI_BIN.
X#
XDESTDIR = $(CGI_BIN)
X
Xuncgi: uncgi.c
X	$(CC) $(CFLAGS) -DCGI_BIN='"$(CGI_BIN)"' -o $@ uncgi.c
X
Xinstall: uncgi
X	cp uncgi $(DESTDIR)
X	chmod 755 $(DESTDIR)/uncgi
X
Xshar:
X	shar README patchlevel.h Makefile uncgi.c > uncgi.shar
END_OF_FILE
if test 521 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'uncgi.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'uncgi.c'\"
else
echo shar: Extracting \"'uncgi.c'\" \(8558 characters\)
sed "s/^X//" >'uncgi.c' <<'END_OF_FILE'
X/*
X * @(#)uncgi.c	1.7 10/19/94
X *
X * Unescape all the fields in a form and stick them in the environment
X * so they can be used without awful machinations.
X *
X * Call with an ACTION such as:
X *	http://foo.bar.com/cgi-bin/uncgi/myscript/extra/path/stuff
X *
X * Uncgi will run "myscript" from the cgi-bin directory, and set PATH_INFO
X * to "/extra/path/stuff".
X *
X * Environment variable names are "WWW_" plus the field name.
X *
X * Copyright 1994, Steven Grimm <koreth@hyperion.com>.
X *
X * Permission is granted to redistribute freely and use for any purpose,
X * commercial or private, so long as this copyright notice is retained
X * and the source code is included free of charge with any binary
X * distributions.
X */
X#include <stdio.h>
X#include <ctype.h>
X
Xvoid *malloc(), *realloc();
Xchar *getenv();
X
Xextern char *sys_errlist[];
Xextern int errno;
X
X#define PREFIX	"WWW_"
X#define ishex(x) (((x) >= '0' && (x) <= '9') || ((x) >= 'a' && (x) <= 'f') || \
X		  ((x) >= 'A' && (x) <= 'F'))
X
X/*
X * Convert two hex digits to a value.
X */
Xstatic int
Xhtoi(s)
X	unsigned char	*s;
X{
X	int	value;
X	char	c;
X
X	c = s[0];
X	if (isupper(c))
X		c = tolower(c);
X	value = (c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10) * 16;
X
X	c = s[1];
X	if (isupper(c))
X		c = tolower(c);
X	value += c >= '0' && c <= '9' ? c - '0' : c - 'a' + 10;
X
X	return (value);
X}
X
X/*
X * Get rid of all the URL escaping in a string.  Modify it in place, since
X * the result will always be equal in length or smaller.
X */
Xstatic void
Xurl_unescape(str)
X	unsigned char	*str;
X{
X	unsigned char	*dest = str;
X
X	while (str[0])
X	{
X		if (str[0] == '+')
X			dest[0] = ' ';
X		else if (str[0] == '%' && ishex(str[1]) && ishex(str[2]))
X		{
X			dest[0] = (unsigned char) htoi(str + 1);
X			str += 2;
X		}
X		else
X			dest[0] = str[0];
X
X		str++;
X		dest++;
X	}
X
X	dest[0] = '\0';
X}
X
X/*
X * Print the start of an error message.
X */
Xstatic void
Xhttp_head()
X{
X	puts("Content-type: text/html\n");
X	puts("<html><head><title>Error!</title></head><body>");
X	puts("<h1>An error has occurred while processing your request.</h1>");
X}
X
X/*
X * Print an HTML error string with the right HTTP header and exit.
X */
Xstatic void
Xhtml_perror(str)
X	char	*str;
X{
X	http_head();
X
X	puts("<p>The following error was encountered while processing");
X	puts("your query.");
X	puts("<blockquote>");
X	printf("%s: %s\n", str, sys_errlist[errno]);
X	puts("</blockquote>");
X	puts("<p>Please try again later.");
X	puts("</body></html>");
X
X	exit(1);
X}
X
X/*
X * Stuff a URL-unescaped variable, with the prefix on its name, into the
X * environment.  Uses the "=" from the CGI arguments.  Putting an "=" in
X * a field name is probably a bad idea.
X *
X * If the variable is already defined, append a '#' to it along with the
X * new value.
X */
Xstatic void
Xstuffenv(var)
X	char	*var;
X{
X	char	*buf, *c, *oldval, *newval;
X
X	url_unescape(var);
X
X	/*
X	 * Allocate enough memory for the variable name and its value.
X	 */
X	buf = malloc(strlen(var) + sizeof(PREFIX) + 1);
X	if (buf == NULL)
X		html_perror("stuffenv");
X
X	strcpy(buf, PREFIX);
X	strcpy(buf + sizeof(PREFIX) - 1, var);		/* save a few cycles */
X
X	/*
X	 * If, for some reason, there wasn't an = in the query string,
X	 * add one so the environment will be valid.
X	 */
X	for (c = buf; *c != '\0'; c++)
X		if (*c == '=')
X			break;
X	if (*c == '\0')
X		c[1] = '\0';
X	
X	/*
X	 * Check for the presence of the variable.
X	 */
X	*c = '\0';
X	if (oldval = getenv(buf))
X	{
X		newval = malloc(strlen(oldval) + strlen(buf) + strlen(c+1) + 2);
X		if (newval == NULL)
X			html_perror("stuffenv: append");
X		
X		*c = '=';
X		strcpy(newval, buf);
X		strcat(newval, "#");
X		strcat(newval, oldval);
X		free(buf);
X	}
X	else
X	{
X		*c = '=';
X		newval = buf;
X	}
X
X	putenv(newval);
X}
X
X/*
X * Scan a query string, stuffing variables into the environment.  This
X * should ideally just use strtok(), but that's not available everywhere.
X */
Xstatic void
Xscanquery(q)
X	char	*q;
X{
X	char	*next = q;
X
X	do {
X		while (*next && *next != '&')
X			next++;
X		if (! *next)
X			next = NULL;
X		else
X			*next = '\0';
X		
X		stuffenv(q);
X		if (next)
X			*next++ = '&';
X		q = next;
X	} while (q != NULL);
X}
X
X/*
X * Read a POST query from standard input into a dynamic buffer.  Terminate
X * it with a null character.
X */
Xstatic char *
Xpostread()
X{
X	char	*buf = NULL;
X	int	size = 0, sofar = 0, got;
X
X	buf = getenv("CONTENT_TYPE");
X	if (buf == NULL || strcmp(buf, "application/x-www-form-urlencoded"))
X	{
X		http_head();
X		puts("<p>No content type was passed to uncgi.</body></html>");
X		exit(1);
X	}
X
X	buf = getenv("CONTENT_LENGTH");
X	if (buf == NULL)
X	{
X		http_head();
X		puts("<p>The server did not tell uncgi how long the request");
X		puts("was.</body></html>");
X		exit(1);
X	}
X	
X	size = atoi(buf);
X	buf = malloc(size);
X	if (buf == NULL)
X		html_perror("postread");
X	do
X	{
X		got = fread(buf + sofar, 1, size - sofar, stdin);
X		sofar += got;
X	} while (got && sofar < size);
X
X	buf[sofar] = '\0';
X
X	return (buf);
X}
X
X/*
X * Run a shell script.  We use this instead of the OS's "#!" mechanism
X * because that mechanism doesn't work too well on SVR3-based systems.
X */
Xvoid
Xrunscript(shell, script)
X	char	*shell, *script;
X{
X	char	*argvec[4], *space;
X	int	pos;
X
X	if (shell[0] != '/')
X		return;
X	
X	pos = strlen(shell) - 1;
X	if (shell[pos] == '\n')
X		shell[pos] = '\0';
X
X	argvec[0] = shell;
X
X	/*
X	 * See if there's an argument string.  strchr() isn't available
X	 * everywhere, so do it ourselves.
X	 */
X	for (space = shell; *space; space++)
X		if (*space == ' ')
X			break;
X
X	if (*space)
X	{
X		*space = '\0';
X		argvec[1] = space + 1;
X		argvec[2] = script;
X		argvec[3] = NULL;
X	}
X	else
X	{
X		argvec[1] = script;
X		argvec[2] = NULL;
X	}
X
X	execv(shell, argvec);
X	/* Fall back to main() on error. */
X}
X
X/*
X * Main program, optionally callable as a library function.
X */
Xvoid
X#ifdef NO_MAIN
Xuncgi()
X#else
Xmain(argc, argv)
X	int	argc;
X	char	**argv;
X#endif
X{
X	char	*query, *program, *pathinfo, *newpathinfo, *method;
X	char	*ptrans, *ptend;
X	char	*argvec[2], shellname[200];
X	int	proglen;
X	FILE	*fp;
X
X	/*
X	 * First, get the query string, wherever it is, and stick its
X	 * component parts into the environment.  Allow combination
X	 * GET and POST queries, even though that's a bit strange.
X	 */
X	query = getenv("QUERY_STRING");
X	if (query != NULL && strlen(query))
X		scanquery(query);
X
X	method = getenv("REQUEST_METHOD");
X	if (method != NULL && ! strcmp(method, "POST"))
X	{
X		query = postread();
X		if (query[0] != '\0')
X			scanquery(query);
X	}
X
X	if (query == NULL)
X	{
X		http_head();
X		puts("<p>The 'uncgi' program couldn't find a query to");
X		puts("process.\n</body></html>");
X		exit(1);
X	}
X
X#ifndef NO_MAIN
X	/*
X	 * Now figure out which program the caller *really* wants, and adjust
X	 * PATH_INFO and PATH_TRANSLATED to look right to that program.
X	 */
X	pathinfo = getenv("PATH_INFO");
X	if (pathinfo != NULL && pathinfo[0])
X	{
X		proglen = 1;	/* path_info always starts with '/' */
X
X		while (pathinfo[proglen] && pathinfo[proglen] != '/')
X			proglen++;
X
X		program = malloc(proglen + sizeof(CGI_BIN));
X		if (program == NULL)
X			html_perror("program");
X		strcpy(program, CGI_BIN);
X		strncat(program + sizeof(CGI_BIN) - 1, pathinfo, proglen);
X
X		/*
X		 * Strip "program" from PATH_TRANSLATED.
X		 * XXX - this depends on strcpy() copying front to back.
X		 */
X		ptrans = getenv("PATH_TRANSLATED");
X		if (ptrans != NULL)
X		{
X			ptend = ptrans + strlen(ptrans) - strlen(pathinfo);
X			strcpy(ptend, ptend + proglen);
X			ptend = malloc(strlen(ptrans) +
X					sizeof("PATH_TRANSLATED="));
X			strcpy(ptend, "PATH_TRANSLATED=");
X			strcat(ptend, ptrans);
X			putenv(ptend);
X		}
X
X		pathinfo += proglen;
X		newpathinfo = malloc(strlen(pathinfo) + sizeof("PATH_INFO="));
X		if (newpathinfo == NULL)
X			html_perror("newpathinfo");
X		strcpy(newpathinfo, "PATH_INFO=");
X		strcat(newpathinfo, pathinfo);
X		putenv(newpathinfo);
X
X
X	}
X	else
X	{
X		/*
X		 * No PATH_INFO means no program to run.
X		 */
X		http_head();
X		puts("<p>Whoever wrote this form doesn't know how to use");
X		puts("the 'uncgi' program, because they didn't tell it");
X		puts("what to run.");
X		puts("<h5>(Bummer.)</h5></body></html>");
X
X		exit(0);
X	}
X
X	/*
X	 * SVR3-based systems seem to have trouble running shell scripts.
X	 * So if that's what this is, run its shell explicitly.
X	 */
X	fp = fopen(program, "r");
X	if (fp != NULL)
X	{
X		if (fgets(shellname, sizeof(shellname), fp) != NULL)
X		{
X			fclose(fp);
X			if (shellname[0] == '#' && shellname[1] == '!')
X				runscript(shellname + 2, program);
X		}
X		fclose(fp);
X	}
X
X	/*
X	 * Now execute the program.
X	 */
X	argvec[0] = program;
X	argvec[1] = NULL;
X	execv(program, argvec);
X
X	/*
X	 * If we get here, the exec failed.
X	 */
X	html_perror(program);
X#endif
X}
END_OF_FILE
if test 8558 -ne `wc -c <'uncgi.c'`; then
    echo shar: \"'uncgi.c'\" unpacked with wrong size!
fi
# end of 'uncgi.c'
fi
echo shar: End of shell archive.
exit 0
